package Robot;

import be.kuleuven.cs.som.annotate.*;


/**
 * A class of robots involving an amount of energy, an energy capacity limit, position coordinates
 * and some facilities for turning and moving.
 * 
 * @invar	The amount of energy stored in the robot must be a valid amount for any robot.
 * 			| IsValidAmountOfEnergy(getAmountOfEnergy(),getEnergyCapacityLimit())
 * 
 * @invar	The capacity limit of energy that can be stored in the robot must be a valid capacity.
 * 			| isValidEnergyCapacityLimit(getEnergyCapacityLimit())
 * @invar	The position of the robot must be within the boundaries
 * 			| isValidPosition(getXPosition()) && isValidPosition(getYPosition())
 * 
 * @version 24-02-2012
 * @author Matthias Moulin & Ruben Pieters
 *
 */
public class Robot implements IRobot {
	
	/**
	 * Initializes this new robot with given amount of energy and given capacity limit
	 * of energy.
	 * 
	 * @param amountOfEnergy		The amount of energy that has to be stored in this robot.
	 * @param energyCapacityLimit	The capacity limit of energy for this robot.
	 * 
	 * @pre		The given amount of energy stored in the robot must be a valid amount.
	 * 			| IsValidAmountOfEnergy(amountOfEnergy,energyCapacityLimit)
	 * @pre		The given capacity limit of energy must be a valid capacity.
	 * 			| isValidEnergyCapacityLimit(energyCapacityLimit)
	 * @effect	The new amount of energy stored in this robot is equal 
	 * 			to the given value for the amount of energy.
	 * 			| new.getAmountOfEnergy() == amountOfEnergy
	 * @effect	The new capacity limit of energy for this robot is equal
	 * 			to the given value for the capacity limit of energy.
	 * 			| new.getEnergyCapacityLimit() == energyCapacityLimit
	 */
	public Robot(long amountOfEnergy, long energyCapacityLimit)
	{
		setAmountOfEnergy(amountOfEnergy);
		setEnergyCapacityLimit(energyCapacityLimit);
	}

	
	// The amount of energy stored in this robot.
	private long amountOfEnergy;
	
	/**
	 * Checks whether the given energy request is a valid amount of energy according
	 * the given capacity request for any robot.
	 * 
	 * @pre 					The given capacity request is a valid capacity 
	 * 							limit of energy for any robot.
	 * 							| isValidEnergyCapacity(capacityRequest)
	 * 
	 * @param energyRequest		The amount of energy that has to be checked.
	 * @param capacityRequest	The capacity limit of energy that's used as reference.
	 * @return					True if and only if the energy request is zero or positive
	 * 							and less than or equal to the capacity request.
	 * 							| result == (energyRequest >=0) && (energyRequest <= capacityRequest)
	 */
	public static boolean isValidAmountOfEnergy(long energyRequest, long capacityRequest)
	{
		return (energyRequest >=0) && (energyRequest <= capacityRequest);
	}
	
	/**
	 * Checks whether this robot can have the given request as its amount of energy.
	 * 
	 * @param request	The amount of energy that has to be checked
	 * @return			True if and only if the request is zero or positive
	 * 					and less than or equal to the capacity limit of energy of this robot.
	 * 					| result == (request >=0) && (request <= getEnergyCapacityLimit())
	 */
	public boolean canHaveHasAmountOfEnergy(long request)
	{
		return (request >=0) && (request <= getEnergyCapacityLimit());
	}
	
	/**
	 * Checks whether the given request is a possible amount of energy for any robot.
	 * 
	 * @param request	The amount of energy that has to be checked
	 * @return			True if and only if the request is zero or positive.
	 * 					| result == (request >=0)
	 */
	public static boolean isPossibleAmountOfEnergy(long request)
	{
		return (request >=0);
	}
	
	/**
	 *Returns the amount of energy stored in this robot.
	 */
	@Basic
	public long getAmountOfEnergy()
	{
		return amountOfEnergy;
	}
	
	/**
	 * Returns the percentage of energy stored in this robot according
	 * it's energy capacity limit.
	 * 
	 * @return 	The percentage of energy stored in this robot according
	 * 		   	it's energy capacity limit.
	 * 		   	| result == (getAmountOfEnergy()/getEnergyCapacityLimit())*100
	 */
	public double getEnergyStoragePercentage()
	{
		double amount = getAmountOfEnergy(); 
		double capacity = getEnergyCapacityLimit();
		return (amount/capacity)*100;
	}
	
	/**
	 * Sets the amount of energy of this robot to the given amount.
	 * 
	 * @pre  	The given amount must be a valid amount of energy for this robot.
	 * 		 	| isValidAmountOfEnergy(amount, getEnergyCapacityLimit())
	 * 
	 * @param amount	The new value for the amount of energy of this robot.
	 * 
	 * @post The given value amount is the new value for the amount of energy of this robot.
	 * 		 | new.getAmountOfEnergy() == amount
	 */
	public void setAmountOfEnergy(long amount)
	{
		assert(isValidAmountOfEnergy(amount, getEnergyCapacityLimit()));
		amountOfEnergy = amount;
	}
	
	/**
	 * Recharges the amount of energy stored in this robot by the given recharge amount.
	 * 
	 * @pre		The recharge amount has to be zero or positive.
	 * 			| rechargeAmount >= 0
	 * @pre 	The sum of the amount already stored in this robot and the given recharge amount
	 * 			must be less than or equal to the capacity limit of energy of this robot.
	 * 			| this.getAmountOfEnergy() + rechargeAmount <= this.getEnergyCapacityLimit()
	 * 
	 * @param 	rechargeAmount	The amount of energy to add to the amount of energy stored
	 * 							in this robot.
	 * 
	 * @effect 	The amount of energy of this robot is set to the sum of the amount already stored 
	 *         	in this robot and the given recharge amount.
	 *         	| setAmountOfEnergy(this.getAmountOfEnergy()+rechargeAmount)
	 */
	public void rechargeEnergy(long rechargeAmount)
	{
		assert(rechargeAmount >= 0);
		assert((this.getAmountOfEnergy() + rechargeAmount) <= this.getEnergyCapacityLimit());
		setAmountOfEnergy(this.getAmountOfEnergy()+rechargeAmount);
	}
	
	/**
	 * Discharges the amount of energy stored in this robot by the given discharge amount.
	 * 
	 * @pre		The discharge amount has to be zero or positive.
	 * 			| dischargeAmount >= 0
	 * @pre 	The given discharge amount must be less than or equal to the amount of
	 * 			energy stored in this robot.
	 * 			| dischargeAmount <= this.getAmountOfEnergy()
	 * 
	 * @param 	dischargeAmount	The amount of energy to extract of the amount of energy stored
	 * 							in this robot.
	 * 
	 * @effect 	The amount of energy of this robot is set to the difference of the amount already stored 
	 *         	in this robot and the given discharge amount.
	 *         	| setAmountOfEnergy(this.getAmountOfEnergy()-dischargeAmount)
	 */
	public void dischargeEnergy(long dischargeAmount)
	{
		assert(dischargeAmount >= 0);
		assert(dischargeAmount <= this.getAmountOfEnergy());
		setAmountOfEnergy(this.getAmountOfEnergy()-dischargeAmount);
	}
	
	
	// The capacity limit of energy which could be stored maximum in this robot.
	// Note that the capacity limit of energy isn't necessary constant in time and possibly differs
	// from robot to robot.
	private long energyCapacityLimit;
	
	/**
	 * Checks whether the given request is a valid capacity limit of energy for this robot.
	 * 
	 * @param request	The capacity that has to be checked
	 * @return 			True if and only if the request is zero or positive.
	 * 					| result == (request >=0)
	 */
	public static boolean isValidEnergyCapacityLimit(long request)
	{
		return (request >=0);
	}
	
	/**
	 * Returns the capacity limit of energy for this robot.
	 * 	The capacity limit of energy expresses the maximum value of energy which
	 * 	can be stored in the robot. Note that the capacity limit of energy isn't 
	 *  necessary constant in time and possibly differs from robot to robot.
	 */
	@Basic
	public long getEnergyCapacityLimit()
	{
		return energyCapacityLimit;
	}
	
	/**
	 * Sets the capacity limit of energy of this robot to the given capacity.
	 * 
	 * @pre  	The given capacity must be a valid capacity limit of energy for this robot.
	 * 		 	| isValidEnergyCapacityLimit(capacity)
	 * 
	 * @param amount	The new value for the capacity limit of energy of this robot.
	 * 
	 * @post The given value capacity is the new value for the capacity limit of energy of this robot.
	 * 		 | new.getEnergyCapacityLimit() == capacity
	 */
	public void setEnergyCapacityLimit(long capacity)
	{
		assert(isValidEnergyCapacityLimit(capacity));
		energyCapacityLimit = capacity;
	}
	
	
	
	
	
	
	
	
	
	
	
	/**
	 * The constructor for this robot with given x coordinate and y coordinate
	 * 
	 * @param xPos the x coordinate to give to the robot
	 * @param yPos the y coordinate to give to the robot
	 * @effect this constructor has the same effect as the moveTo(xPos, yPos) method
	 * 		| moveTo(xPos, yPos)
	 */
	public Robot(long xPos, long yPos, Object dummy){
		moveTo(xPos, yPos);
	}
	
	/**
	 * Checks if the given coordinate is a valid coordinate
	 * 
	 * @param pos The coordinate to check
	 * @return True if the position is positive or equal to zero
	 * 		| result == (pos >= 0)
	 */
	public static boolean isValidPosition(long pos){
		return (pos >= 0) && (pos <= Long.MAX_VALUE);
	}
	
	/**
	 * Translates the coordinates with the given amounts x and y
	 * 
	 * @param xTranslation The amount to translate in the x direction
	 * @param yTranslation The amount to translate in the y direction
	 * @effect Has the same effect as the method translateX(xTranslation) followed by translateY(yTranslation)
	 * 		| translateX(xTranslation)
	 * 		| translateY(yTranslation)
	 */
	public void translate(long xTranslation, long yTranslation) throws IllegalPositionException{
		translateX(xTranslation);
		translateY(yTranslation);
	}
	
	/**
	 * Moves the coordinates to the given amounts x and y
	 * 
	 * @param xPos the x position to move to
	 * @param yPos the y position to move to
	 * @effect Has the same effect as the method movetoX(xPos) followed by moveToY(yPos)
	 * 		| moveToX(xPos)
	 * 		| moveToY(yPos)
	 */
	public void moveTo(long xPos, long yPos){
		moveToX(xPos);
		moveToY(yPos);
	}
	
	/**
	 * Translates the x coordinate with the given amount
	 * 
	 * @param xTranslation The amount to translate in the x direction
	 * @post The new x position is equal to the old position plus the translation amount
	 * 		| new.getXPosition() == getXPosition() + xTranslation
	 * @throws IllegalPositionException
	 * 		The new x position results in an invalid position
	 * 		| !isValidPosition(xPosition + xTranslation)
	 */
	public void translateX(long xTranslation) throws IllegalPositionException{
		if (!isValidPosition(getXPosition() + xTranslation)){
			throw new IllegalPositionException(getXPosition() + xTranslation);
		}
		moveToX(getXPosition() + xTranslation);
	}
	
	/**
	 * Moves the x position to the given x coordinate
	 * 
	 * @param xPos the x position to move to
	 * @post The new x position is equal to the given position
	 * 		| new.getXPosition() == xPos
	 * @throws IllegalPositionException
	 * 		The given x position is an invalid position
	 * 		| !isValidPosition(xPos)
	 */
	public void moveToX(long xPos) throws IllegalPositionException{
		if (!isValidPosition(xPos)){
			throw new IllegalPositionException(xPos);
		}
		xPosition = xPos;
	}
	
	/**
	 * Returns the x coordinate
	 */
	@Basic
	public long getXPosition(){
		return xPosition;
	}
	
	/**
	 * Variable to store the x coordinate
	 */
	private long xPosition;
	
	/**
	 * Translates the y coordinate with the given amount
	 * 
	 * @param yTranslation The amount to translate in the y direction
	 * @post  The new y position is equal to the old position plus the translation amount
	 * 		| new.getYPosition() == getYPosition() + yTranslation
	 * @throws IllegalPositionException
	 * 		The new y position results in an invalid position
	 * 		| !isValidPosition(yPosition + yTranslation)
	 */
	public void translateY(long yTranslation) throws IllegalPositionException{
		if (!isValidPosition(getYPosition() + yTranslation)){
			throw new IllegalPositionException(getXPosition() + yTranslation);
		}
		moveToY(getYPosition() + yTranslation);
	}

	/**
	 * Moves the y position to the given y coordinate
	 * 
	 * @param yPos The y position to move to
	 * @post The new y position is equal to the given position
	 * 		| new.getYPosition() == yPos
	 * @throws IllegalPositionException
	 * 		The given y position is an invalid position
	 * 		| !isValidPosition(yPos)
	 */
	public void moveToY(long yPos) throws IllegalPositionException{
		if (!isValidPosition(yPos)){
			throw new IllegalPositionException(yPos);
		}
		yPosition = yPos;
	}
	
	/**
	 * Returns the y coordinate
	 */
	@Basic
	public long getYPosition(){
		return yPosition;
	}
	
	/**
	 * Variable to store the x coordinate
	 */
	private long yPosition;

	/**
	 * Turns the robot in a clockwise direction
	 * 
	 * @effect Has the same effect as adding one to the current orientation of the robot and then giving this result
	 * 		to the method setOrientation
	 * 		| setOrienation(getOrientation()+1)
	 */
	public void turnClockwise(){
		setOrientation(getOrientation()+1);
	}
	
	/**
	 * Set the orientation of the robot to the given orientation
	 * 
	 * @param newOrientation the new orientation to apply to the robot
	 * @post new.getOrientation = Math.abs(newOrientation % 4)
	 */
	public void setOrientation(int newOrientation){
		orientation = Math.abs(newOrientation % 4);
	}
	
	/**
	 * Returns the orientation of the robot
	 */
	@Basic
	public int getOrientation(){
		return orientation;
	}
	
	/**
	 * Variable to store the orienation of the robot
	 */
	private int orientation;
	
}
